package com.john.utils.socket;

import com.john.utils.common.StreamUtils;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.SocketChannel;

/**
 * SocketChannel 工具类
 */
public class SocketChannelUtils {
    private static final String TAG = "SocketChannelUtils------";
    private String hostname = "127.0.0.1";
    private int port = 5037;
    private long millis = 500;  //休眠时间
    private boolean mQuit = false;

    public SocketChannelUtils(String hostname, int port) {
        this.hostname = hostname;
        this.port = port;
    }

    public SocketChannelUtils() {

    }

    /**
     * 创建socket客户端实例并连接
     * @return SocketChannel
     */
    public SocketChannel createSocketChanne(String socketName){
        SocketChannel socketChannel = null;
        int count = 0;
        try {
            do {
                count++;
                try {
                    if(socketChannel != null){
                        socketChannel.close();
                    }
                    d(TAG + socketName,"--createSocketChannelClient正在尝试第【"+count+"】次连接..."+ hostname + "-----" + port);
                    socketChannel = SocketChannel.open(new InetSocketAddress(hostname, port));
                    socketChannel.socket().setKeepAlive(true);
                    socketChannel.socket().setTcpNoDelay(true);
                    if (socketChannel.finishConnect()) {
                        break;
                    }
                    Thread.sleep(millis);
                } catch (AsynchronousCloseException e) {
                    socketChannelClose(socketChannel);
                    d(TAG + socketName,"--createSocketChannelClient连接已被强制断开..."+ hostname + "-----" + port);
                } catch (ConnectException e) {
                    socketChannelClose(socketChannel);
                    d(TAG + socketName,"--createSocketChannelClient第【"+count+"】次连接失败..."+ hostname + "-----" + port);
                }
            }while (count < 3 );   //失败重试策略10次
        }catch (Exception e) {
            socketChannelClose(socketChannel);
            d(TAG + socketName,"--createSocketChannelClient未知连接异常..."+ hostname + "-----" + port);
            e.printStackTrace();
        }
        if(socketChannel != null){
            d(TAG + socketName,"--createSocketChannelClient第【"+count+"】次连接成功..." + hostname + "-----" + port);
        }else{
            d(TAG + socketName,"--createSocketChannelClient连接失败...已尝试连接【"+count+"】次..." + hostname + "-----" + port);
        }
        return socketChannel;
    }

    /**
     * 发送数据
     * @param data
     */
    public void sendMessage(SocketChannel socketChannel,String socketName,byte[] data){
        try {
            ByteBuffer buf = ByteBuffer.wrap(data, 0, data.length);
            socketChannel.write(buf);
        } catch (AsynchronousCloseException e) {
            d(TAG + socketName,"--sendMessage连接已被强制断开..." + e.getMessage() +"-----" + hostname + "-----" + port);
            socketChannelClose(socketChannel);
        } catch (IOException e) {
            d(TAG + socketName,"--sendMessage发送数据失败..."+ hostname + "-----" + port);
            socketChannelClose(socketChannel);
        }
    }

    /**
     * 读取SocketChannel数据
     * @return message ByteBuffer
     */
    public ByteBuffer getOutput(SocketChannel socketChannel,String socketName){
        ByteBuffer header = ByteBuffer.allocate(4 * 1024);
        try {
            fillBuffer(header, socketChannel);
            header.flip();
            return header;
        } catch (Exception e) {
            d(TAG + socketName,"--getOutput读取数据失败..." + e.getMessage() +"-----" + hostname + "-----" + port);
            socketChannelClose(socketChannel);
        }
        return null;
    }

    /**
     * 读取SocketChannel数据
     * @return message String
     */
    public String getOutputString(SocketChannel socketChannel,String socketName){
        ByteBuffer header = ByteBuffer.allocate(4 * 1024);
        try {
            fillBuffer(header, socketChannel);
            header.flip();
            return StreamUtils.byteTOString(header.array());
        } catch (Exception e) {
            d(TAG + socketName,"--getStringOutput读取数据失败..." + e.getMessage() +"-----" +  hostname + "-----" + port);
            socketChannelClose(socketChannel);
        }
        return null;
    }

    /**
     * 连续读取SocketChannel信息
     */
    public void getCallbackOutput(SocketChannel socketChannel,String socketName,UpdateMessageListener updateMessageListener){
        try {
            if(updateMessageListener != null){
                new Thread(new SocketChannelMessageTask(socketChannel,socketName,updateMessageListener), "SocketChannel message read").start();
                d(TAG + socketName,"getCallbackOutput连续读取数据进程启动..."+ hostname + "-----" + port);
            }else{
                d(TAG + socketName,"getCallbackOutput连续读取数据回调接口实例《UpdateMessageListener》不能为空..."+ hostname + "-----" + port);
            }
        } catch (Exception e) {
            d(TAG + socketName,"--getCallbackOutput读取数据失败..." + e.getMessage() +"-----" +  hostname + "-----" + port);
            socketChannelClose(socketChannel);
        }
    }

    //SocketChannel数据读取
    private void fillBuffer(ByteBuffer buffer,SocketChannel channel){
        int len;
        try {
            do {
                len = channel.read(buffer);
                if (len == 0) {
                    // 如果没有数据，则稍微等待一下
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
            }while (buffer.hasRemaining() && len != -1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void d(String tag,String msg){
        System.out.println(tag + " : " + msg);
    }

    /**
     * 读取信息回调
     */
    public interface UpdateMessageListener {
        //消息回调
        void messageUpdate(ByteBuffer byteBuffer);
        //如果中途socketChannel异常为null则回调处理
        SocketChannel socketChannelEqNull();
    }

    /**
     * 关闭socket
     */
    public void socketChannelClose(SocketChannel socketChannel){
        try {
            if(socketChannel != null){
                socketChannel.close();
            }
            mQuit = true;
            socketChannel = null;
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * 连续读取socket数据线程
     */
    class SocketChannelMessageTask implements Runnable {
        private SocketChannel socketChannel = null;
        private String socketName;
        private UpdateMessageListener updateMessageListener;

        public SocketChannelMessageTask(SocketChannel socketChannel,String socketName,UpdateMessageListener updateMessageListener) {
            this.socketChannel = socketChannel;
            this.socketName = socketName;
            this.updateMessageListener = updateMessageListener;
        }

        @Override
        public void run() {
            do {
                try {
                    if(socketChannel == null){
                        //如果中途socketChannel异常为null则回调处理
                        socketChannel = updateMessageListener.socketChannelEqNull();
                        Thread.sleep(5L);
                        //如果回调处理后socketChannel任然为null则退出当前读取信息
                        if(socketChannel == null){
                            socketChannelClose(socketChannel); //直接停止读取数据
                        }
                    }
                    if(socketChannel != null){
                        ByteBuffer header = ByteBuffer.allocate(4 * 1024);
                        try {
                            fillBuffer(header, socketChannel);
                            header.flip();
                            if(header.array().length > 0){
                                updateMessageListener.messageUpdate(header);
                            }
                        }catch (Exception e) {
                            e.printStackTrace();
                            System.exit(0);
                            d(TAG + socketName,"SocketChannelMessageTask读取数据失败..." + e.getMessage() + "-----" + hostname + "-----" + port);
                            if(e.getMessage().contains("远程主机强迫关闭了一个现有的连接")){
                                socketChannel = null;
                            }else{
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (Exception var9) {
                    Thread.currentThread().interrupt();
                }
            } while(!mQuit);
        }
    }

}
